<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Description of ORM
 *
 * @author user
 */
class ORM {

    protected $_dbquery;
    protected $_table_name;
    protected $_data = array();
    protected $_result;
    protected $_primary_key;
    protected $_limit = null;
    protected $_where = null;
    protected $_sorting = null;
    protected $_groupby = null;
    protected $_default_sorting = array('id' => 'ASC');

    /**
     * Constructor.
     * If $table not defined use class's name.
     * In the end, construcor read available fields of table
     *
     * @param   string  Table name
     * @return  void
     */
    public function __construct($table = '') {
        $this->_dbquery = & DBQuery::instance();

        if ($table == '') {
            $this->_table_name = strtolower(get_class($this));
        } else {
            $this->_table_name = $table;
        }

        $this->_get_column();
    }

    /**
     * Set field
     * Error will be shown if field name could not be found
     *
     * $model->field = 'value';
     *
     * @param   string  field name
     * @param   string  value
     * @return  void
     */
    public function __set($field_name, $value) {
        $this->_data[$field_name] = $value;
    }

    /**
     * Assign data. Data is assigned with array $fields.
     *
     * $model->set(array('username' => 'user', 'password' => 'pass'))
     *
     * @param   array   fields
     */
    public function set($fields) {
        if (is_array($fields)) {
            foreach ($this->_data as $key => $value) {
                if (isset($fields[$key])) {
                    $this->_data[$key] = $fields[$key];
                }
            }
        }
    }

    /**
     * Get field value
     * Error will be shown if field name could not be found
     *
     * $model->field
     *
     * @param   string  Field name
     * @return  string  Value
     */
    public function __get($field_name) {
        if (!array_key_exists($field_name, $this->_data)) {
            show_error("Field $field_name could not be found at table $this->_table_name");
        }

        return $this->_data[$field_name];
    }

    /**
     * Get table's column
     *
     * @return  void
     */
    private function _get_column() {
        $result = $this->_dbquery->query("SHOW COLUMNS FROM " . $this->_table_name);

        if (!$result) {
            show_error('query error at _get_column');
        }

        if ($result && mysql_num_rows($result) > 0) {
            while ($row = mysql_fetch_assoc($result)) {
                if ($row['Key'] == "PRI") {
                    // Primary Key
                    $this->_primary_key = $row['Field'];
                }

                $this->_data[$row['Field']] = null;
            }
        }
    }

    /**
     * Make all element of _data become null
     *
     * @return void
     */
    private function _reset_data() {
        foreach($this->_data as $i => $val) {
            $this->_data[$i] = null;
        }
    }

    /**
     * Save record.
     * If record is already exist in table, just update it, if not add it.
     *
     * @return  void
     */
    public function save() {
        $primary_key = $this->_primary_key;

        $query = "
            SELECT *
            FROM `$this->_table_name`
            WHERE `$primary_key` = '" . $this->_data[$primary_key] . "'";

        $result = $this->_dbquery->query($query);

        if (isset($result[0])) {
            return $this->_update();
        } else {
            return $this->_add();
        }
    }

    /**
     * Update record
     *
     * @return  void
     */
    private function _update() {
        $set = '';

        // Init 'set'
        foreach ($this->_data as $key => $value) {
            if ($key != $this->_primary_key && $value != null) {
                $set .= " `$key` = '$value',";
            }
        }

        if ($set == '')
            return FALSE;

        // Remove 'comma' at last character
        $set = rtrim($set, ',');

        // Set query
        $query = "
                UPDATE `$this->_table_name`
                SET $set
                WHERE `$this->_primary_key` = '" . $this->_data[$this->_primary_key] . "'";

        /*
         * UPDATE
         */
        if (!$this->_dbquery->query($query))
            return FALSE;

        return TRUE;
    }

    /**
     * Add new record
     *
     * @return  void
     */
    private function _add() {
        $field = "";
        $values = "";

        // Set field and values
        foreach ($this->_data as $key => $value) {
            if ($key != $this->_primary_key && $value != null) {
                $field .= "`$key`,";
                $values .= "'$value',";
            }
        }

        if ($field != '' && $values != '') {

            // Remove 'comma' at last character
            $field = rtrim($field, ',');
            $values = rtrim($values, ',');

            $query = "
            INSERT
            INTO $this->_table_name($field)
            VALUES ($values)";

            // Add
            if (!$this->_dbquery->query($query))
                return FALSE;

            // Set its id
            $this->id = mysql_insert_id(Database::handler());
        } else {
            return FALSE;
        }

        return TRUE;
    }

    /**
     * Delete record
     *
     * @return  void
     */
    public function delete() {
        $query = "
            DELETE
            FROM $this->_table_name
            WHERE `$this->_primary_key` = '" . $this->_data[$this->_primary_key] . "'";

        if($this->_dbquery->query($query)) {
            $this->_data = array();
            return true;
        } else {
            show_error('Delete error' . $query);
            return false;
        }
    }

    /**
     * Find a record with single result
     *
     * $model->find(1) will find record where `id` = 1
     *
     * $model->where("`username` = 'user'")->find();
     *
     * @param   integer id
     * @return  ORM     this object
     */
    public function find($id = null) {
        $query = null;
        $where = null;

        // Reset data
        $this->_reset_data();

        if ($id != null && is_numeric($id)) {
            $where = "`id` = $id";
        } else {
            if ($this->_where != null) {
                $where = $this->_where;
            }
        }

        if ($where != null) {
            $query = "select * from `$this->_table_name` where $where order by " . $this->order_by_string();
            $result = $this->_dbquery->query($query);

            if (isset($result[0])) {
                $this->_data = $result[0][$this->_table_name];
            }
        }

        // Reset limit, where and sorting
        $this->_limit = $this->_where = $this->_sorting = null;

        return $this;
    }

    /**
     * Find all record
     *
     * $result = $user->find_all();
     *
     * foreach($result as $r) {
     *  echo $r->username;
     * }
     *
     * @return  array     array of ORM
     */
    public function find_all() {
        // Reset data
        $this->_reset_data();

        $query = "select * from `$this->_table_name`";

        if ($this->_where != null) {
            $query .= " where $this->_where";
        }

        if ($this->_groupby != null) {
            $query .= " group by $this->_groupby";
        }

        $query .= " order by " . $this->order_by_string();

        if ($this->_limit != null) {
            $query .= " limit $this->_limit";
        }
        
        $query_result = $this->_dbquery->query($query);

        $result = array();
        $model;
        foreach ($query_result as $qr) {
            $model = new ORM($this->_table_name);
            $model->set($qr[$this->_table_name]);

            $result[] = $model;
        }

        // Reset limit, where and sorting
        $this->_limit = $this->_where = $this->_sorting = null;

        return $result;
    }

    /**
     * Query anything to database and return array of ORM (in this type)
     * as a result of that query
     * @param String $query
     * @return Array of ORM (int this type) as a result of query
     */
    public function query($query) {
        $query_result = $this->_dbquery->query($query);

        $result = array();
        $model;
        foreach ($query_result as $qr) {
            $model = new ORM($this->_table_name);
            $model->set($qr[$this->_table_name]);

            $result[] = $model;
        }

        return $result;
    }

    /**
     * Order by
     *
     * $orm->order_by(array('id' => 'ASC', 'name' => 'DESC'))->find_all()
     *
     * @param   array pair of field and type of sorting
     * @return  ORM
     */
    public function order_by($sorting) {
        if (is_array($sorting)) {
            $this->_sorting = $sorting;
        }

        return $this;
    }

    /**
     * Convert _sorting or _default_sorting to string. By befault, _sorting will
     * be converted. If _sorting is not set, _default_sorting will be converted;
     *
     * array('id' => 'DESC', 'name' => 'ASC') become to "`id` DESC, `name` ASC"
     *
     * @return  string
     */
    private function order_by_string() {
        $string = '';
        $sorting = array();

        if ($this->_sorting != null) {
            $sorting = $this->_sorting;
        } else {
            $sorting = $this->_default_sorting;
        }

        foreach ($sorting as $key => $value) {
            $string .= "`$key` $value,";
        }

        // remove last comma
        $string = rtrim($string, ',');

        return $string;
    }

    /**
     * Set limit for this ORM
     *
     * $orm->limit(0,5)->find_all();
     *
     * @param integer   from
     * @param integer   how many
     * @return ORM
     */
    public function limit($start, $offset) {
        $this->_limit = "$start, $offset";
        return $this;
    }

    /**
     * Set where condition
     *
     * $user->where("`location` = 'Indonesia' AND `city` = 'Jakarta'")->
     * find_all(); // return all
     *
     * $userm->where("`location` = 'Indonesia'  AND `city` = 'Jakarta'")->
     * find(); // first result
     *
     * @param   string  where condition
     * @return  ORM
     */
    public function where($condition) {
        $this->_where = $condition;
        return $this;
    }

    public function groupby($condition) {
        $this->_groupby = $condition;
        return $this;
    }

    public function toJsonString() {
        return json_encode($this->_data);
    }

    public function getData() {
        return $this->_data;
    }
}

?>
